﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Security;


namespace PServiceBus.Core.Runtime.Extensions
{
    /// <summary>
    /// Extension for string object
    /// </summary>
    public static class StringExtension
    {
        /// <summary>
        /// Get the underlying string hash code.
        /// This implementation is defined in order to avoid conflict with the redefinition of hash code algorithm in the next .net framework impl
        /// </summary>
        /// <param name="s">The s.</param>
        /// <returns></returns>
        public unsafe static int GetHashCodeEx(this string s) {
            unsafe {
                fixed (char* str = s) {
                    char* chPtr = str;
                    int num = 0x15051505;
                    int num2 = num;
                    int* numPtr = (int*)chPtr;
                    for (int i = s.Length; i > 0; i -= 4) {
                        num = (((num << 5) + num) + (num >> 0x1b)) ^ numPtr[0];
                        if (i <= 2) {
                            break;
                        }
                        num2 = (((num2 << 5) + num2) + (num2 >> 0x1b)) ^ numPtr[1];
                        numPtr += 2;
                    }
                    return (num + (num2 * 0x5d588b65));
                }
            }
        }
        private const string _xmlHeader = @"<?xml version=""1.0""?>";
        /// <summary>
        /// Try to load string into XmlDocument to retrieve the InnerXml content of the loaded string
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static string InnerXml(this string s) {
            var doc = new System.Xml.XmlDocument();
            doc.LoadXml(_xmlHeader + s);
            return doc.ChildNodes[1].InnerXml;
        }

        private const string _xmlBase64Tag = "[xmlbase64]";

        /// <summary>
        /// Returns true if string is an base64 encoded xml
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static bool IsXmlBase64(this string s) {
            return !String.IsNullOrWhiteSpace(s) && s.EndsWith(_xmlBase64Tag);
        }

        /// <summary>
        /// Convert base64 encoded xml to xml
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static string XmlBase64ToXml(this string s) {
            return Encoding.UTF8.GetString(Convert.FromBase64String(s.Replace(_xmlBase64Tag, "")));
        }

        public static string Escape(this string str) {
            var length = str.Length;
            if (str.IndexOf('<', 0, length) > 0 ||
                    str.IndexOf('>', 0, length) > 0 ||
                    str.IndexOf('\'', 0, length) > 0 ||
                    str.IndexOf('"', 0, length) > 0 ||
                    str.IndexOf('&', 0, length) > 0)
                return SecurityElement.Escape(str);
            return str;
        }

        static readonly string[] _escapeString = new[] { "&lt;", "&gt;", "&quot;", "&apos;", "&amp;" };
        static readonly string[] _unEscapeString = new string[] { "<", ">", "\"", "'", "&" };

        public static string UnEscape(this string str) {
            for (var i = 0; i < _escapeString.Length; i++) {
                if (str.IndexOf(_escapeString[i], StringComparison.Ordinal) >= 0)
                    str = str.Replace(_escapeString[i], _unEscapeString[i]);
            }
            return str;
        }

        public static string ReplaceEx(this string original, string pattern, string replacement, 
            StringComparison comparisonType = StringComparison.CurrentCulture, int stringBuilderInitialSize = -1) {
            if (original == null) {
                return null;
            }

            if (String.IsNullOrEmpty(pattern)) {
                return original;
            }


            int posCurrent = 0;
            int lenPattern = pattern.Length;
            int idxNext = original.IndexOf(pattern, comparisonType);
            StringBuilder result = new StringBuilder(stringBuilderInitialSize < 0 ? Math.Min(4096, original.Length) : stringBuilderInitialSize);

            while (idxNext >= 0) {
                result.Append(original, posCurrent, idxNext - posCurrent);
                result.Append(replacement);

                posCurrent = idxNext + lenPattern;

                idxNext = original.IndexOf(pattern, posCurrent, comparisonType);
            }

            result.Append(original, posCurrent, original.Length - posCurrent);

            return result.ToString();
        }
 

        /// <summary>
        /// Convert string to the specific type
        /// </summary>
        /// <param name="value"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        public static object ChangeType(this string value, Type type) {
            return
                type.IsEnum ? Enum.Parse(type, value) :
                type == typeof(byte[]) ? Convert.FromBase64String(value) :
                type == typeof(Guid) ? new Guid(value) :
                type == typeof(TimeSpan) ? TimeSpan.Parse(value) :
                type != typeof(object) && type.GetConstructor(Type.EmptyTypes) != null ? Activator.CreateInstance(type) :
                type == typeof(object) ? Phoenix.ReflectionExtensions.AutomaticTypeConverter.ToExpectedType(value) :
                Convert.ChangeType(value, type);
        }

        static readonly Encoding UTF8 = Encoding.UTF8;
        public static byte[] ToUTF8Bytes(this string value) {
            var size = UTF8.GetByteCount(value);
            var buffer = new byte[size];
            UTF8.GetBytes(value, 0, value.Length, buffer, 0);
            return buffer;
        }

        public static string UTF8ByteToString(this byte[] value) {
            return UTF8.GetString(value);
        }
    }

}
